#ifndef DATA_H
#define DATA_H

#include <QString>
#include <QIcon>
#include <QUuid>
#include <QDateTime>
#include <QFile>
#include <QDebug>
#include <QFileInfo>

// pb头文件找不到的解决方法：重新编译项目
#include "base.qpb.h"

// 关于命名空间的约定：
// 1. 如果代码文件位于项目顶层，使用全局命名空间（不手动指定）
// 2. 如果代码文件位于项目子目录中，指定一个和目录名字相同的命名空间
namespace model {

///////////////////////////////////////////////////////////
/// 日志宏
/// 项目日志输入方式
///////////////////////////////////////////////////////////
static inline QString getFileNameFromPath(const QString &path) {
    QFileInfo fileInfo(path);
    return fileInfo.fileName();
}

#define TAG QString("[%1:%2]").arg(model::getFileNameFromPath(__FILE__), QString::number(__LINE__))
#define LOG() qDebug().noquote() << TAG

///////////////////////////////////////////////////////////
/// 工具函数模块
/// 该部分函数写在 .h 中，因此必须加 static inline
/// 目的是为了避免在程序链接阶段出现“函数重定义”报错
///////////////////////////////////////////////////////////

///
/// \brief makeMessageId 生成唯一消息标示
/// \return 唯一消息标示
///
static inline QString makeMessageId() {
    return "M" + QUuid::createUuid().toString().sliced(25, 12); // 唯一消息标示格式：M+后12位UUID
}

///
/// \brief makeFormatTime 生成格式为 "MM-dd HH:mm:ss" 的格式化时间字符串
/// \param timestamp 时间戳
/// \return 格式化时间字符串
///
static inline QString makeFormatTime(int64_t timestamp) {
    QDateTime datetime = QDateTime::fromSecsSinceEpoch(timestamp);
    return datetime.toString("MM-dd HH:mm:ss");
}

///
/// \brief getTime 获取秒级时间戳
/// \return
///
static inline int64_t getTime() {
    return QDateTime::currentSecsSinceEpoch();
}

///
/// \brief makeIcon 根据 byteArray 构造 Icon
/// \param byteArray 传入指定的 byteArray
/// \return Icon
///
static inline QIcon makeIcon(const QByteArray &byteArray) {
    QPixmap pixmap;
    pixmap.loadFromData(byteArray);
    QIcon icon(pixmap);
    return icon;
}

///
/// \brief readByteArrayFromFile 从文件中读取二进制
/// \param path 文件路径
/// \return 字节数组
///
static inline QByteArray readByteArrayFromFile(const QString &path) {
    QFile file(path);
    bool ok = file.open(QFile::ReadOnly);
    if (false == ok) {
        qDebug() << "文件打开失败！";
        return QByteArray();
    }
    QByteArray content = file.readAll();
    file.close();
    return content;
}

///
/// \brief writeByteArrayToFile 往文件中写如字节数组
/// \param path 文件路径
/// \param content 字节数组
///
static inline void writeByteArrayToFile(const QString &path, const QByteArray &content) {
    QFile file(path);
    bool ok = file.open(QFile::WriteOnly);
    if (false == ok) {
        qDebug() << "文件打开失败！";
    }
    file.write(content);
    file.flush(); // 为什么要刷新缓冲区?保证缓冲区中的数据被写入文件
    file.close();
}

///////////////////////////////////////////////////////////
/// 用户信息
///////////////////////////////////////////////////////////

class UserInfo
{
public:
    QString userId = "";        // 用户id
    QString nickname = "";      // 用户签名
    QString description = "";   // 用户签名
    QString phone = "";         // 手机号码
    QIcon avatar;               // 用户头像


    // 从 protobuffer 的 UserInfo 对象, 转成当前代码的 UserInfo 对象
    // bite_im 是 pb 生成的命名空间
    void load(const bite_im::UserInfo& userInfo)
    {
        this->userId = userInfo.userId();
        this->nickname = userInfo.nickname();
        this->phone = userInfo.phone();
        this->description = userInfo.description();
        if (userInfo.avatar().isEmpty())
        {
            // 没有设置头像的新用户使用默认头像
            this->avatar = QIcon(":/resource/image/defaultAvatar.png");
        }
        else
        {
            this->avatar = makeIcon(userInfo.avatar());
        }
    }
}; // end UserInfo

///////////////////////////////////////////////////////////
/// 消息类型枚举
///////////////////////////////////////////////////////////

enum MessageType {
    TEXT_TYPE,      // 文本消息
    IMAGE_TYPE,     // 图片消息
    FILE_TYPE,      // 文件消息
    SPEECH_TYPE     // 语音消息
}; // end MessageType

///////////////////////////////////////////////////////////
/// 消息信息
///////////////////////////////////////////////////////////
class Message {
public:
    QString messageId = "";             // 消息id
    QString chatSessionId = "";         // 消息所属的会话id，会话对消息=1对多
    QString time = "";                  // 消息的时间，格式化时间字符串，如：06-07 12:00:00
    MessageType messageType = TEXT_TYPE; // 消息类型，默认为文本消息
    UserInfo sender;                    // 消息发送者
    QByteArray content;                 // 消息正文，如果是文本消息，正文是字符串；如果是其他类型消息，正文是特定格式的二进制序列
    QString fileId = "";                // 文件身份标示，非文本 类型消息时该字段生效，否则该字段为空串
    QString fileName = "";              // 文件名，文件 类型消息时该字段生效，否则该字段为空串

    ///
    /// \brief makeMessage 工厂方法，根据传入的消息类型构造一条消息
    /// \param messageType 消息类型
    /// \param chatSessionId 消息所属会话标示
    /// \param sender 消息发送者
    /// \param conent 消息正文
    /// \param extraInfo 该字段目前仅在消息类型为文件消息时，作为“文件名”补充
    /// \return 返回消息类型对应的消息对象
    ///
    static Message makeMessage(MessageType messageType, const QString& chatSessionId, const UserInfo& sender, const QByteArray& content, const QString& extraInfo) {
        switch(messageType) {
        case TEXT_TYPE:
            return makeTextMessage(chatSessionId, sender, content);
        case IMAGE_TYPE:
            return makeImageMessage(chatSessionId, sender, content);
        case FILE_TYPE:
            return makeFileMessage(chatSessionId, sender, content, extraInfo);
        case SPEECH_TYPE:
            return makeSpeechMessage(chatSessionId, sender, content);
        default:
            return Message(); // 其余消息类型
        }
    }

    void load(const bite_im::MessageInfo& messageInfo)
    {
        this->messageId = messageInfo.messageId();
        this->chatSessionId = messageInfo.chatSessionId();
        this->time = makeFormatTime(messageInfo.timestamp());
        this->sender.load(messageInfo.sender());

        // 设置消息类型
        auto type = messageInfo.message().messageType();
        if (type == bite_im::MessageTypeGadget::MessageType::STRING)
        {
            // 设置文本消息类型
            this->messageType = TEXT_TYPE;
            // 转UTF8字符串
            this->content = messageInfo.message().stringMessage().content().toUtf8();
        }
        else if (type == bite_im::MessageTypeGadget::MessageType::IMAGE)
        {
            // 设置图片消息类型
            this->messageType = IMAGE_TYPE;

            if (messageInfo.message().imageMessage().hasImageContent())
            {
                this->content = messageInfo.message().imageMessage().imageContent();
            }

            if (messageInfo.message().imageMessage().hasFileId())
            {
                this->fileId = messageInfo.message().imageMessage().fileId();
            }
        }
        else if (type == bite_im::MessageTypeGadget::MessageType::FILE)
        {
            // 设置文件消息类型
            this->messageType = FILE_TYPE;

            if (messageInfo.message().fileMessage().hasFileContents())
            {
                this->content = messageInfo.message().fileMessage().fileContents();
            }

            if (messageInfo.message().fileMessage().hasFileId())
            {
                this->fileId = messageInfo.message().fileMessage().fileId();
            }

            this->fileName = messageInfo.message().fileMessage().fileName();
        }
        else if (type == bite_im::MessageTypeGadget::MessageType::SPEECH)
        {
            // 设置语音消息类型
            this->messageType = SPEECH_TYPE;

            if (messageInfo.message().speechMessage().hasFileContents())
            {
                this->content = messageInfo.message().speechMessage().fileContents();
            }

            if (messageInfo.message().speechMessage().hasFileId())
            {
                this->fileId = messageInfo.message().speechMessage().fileId();
            }
        }
        else
        {
            // 错误的类型, 啥都不做了, 只是打印一个日志
            LOG() << "非法的消息类型! type=" << type;
        }
    }

private:
    static Message makeTextMessage(const QString &chatSessionId, const UserInfo &sender, const QByteArray &content) {
        Message message;

        message.messageId = makeMessageId();
        message.messageType = TEXT_TYPE;
        message.chatSessionId = chatSessionId;
        message.sender = sender;
        message.content = content;
        message.time = makeFormatTime(getTime());
        message.fileId = ""; // 不使用
        message.fileName = ""; // 不使用

        return message;
    }
    static Message makeImageMessage(const QString &chatSessionId, const UserInfo &sender, const QByteArray &content) {
        Message message;

        message.messageId = makeMessageId();
        message.messageType = IMAGE_TYPE;
        message.chatSessionId = chatSessionId;
        message.sender = sender;
        message.content = content;
        message.time = makeFormatTime(getTime());
        message.fileId = ""; // 目前先设置为空字符串，后续通过其他途径设置
        message.fileName = ""; // 不使用

        return message;
    }
    static Message makeFileMessage(const QString &chatSessionId, const UserInfo &sender, const QByteArray &content, const QString &fileName) {
        Message message;

        message.messageId = makeMessageId();
        message.messageType = FILE_TYPE;
        message.chatSessionId = chatSessionId;
        message.sender = sender;
        message.content = content;
        message.time = makeFormatTime(getTime());
        message.fileId = ""; // 目前先设置为空字符串，后续通过其他途径设置
        message.fileName = fileName;

        return message;
    }
    static Message makeSpeechMessage(const QString &chatSessionId, const UserInfo &sender, const QByteArray &content) {
        Message message;

        message.messageId = makeMessageId();
        message.messageType = SPEECH_TYPE;
        message.chatSessionId = chatSessionId;
        message.sender = sender;
        message.content = content;
        message.time = makeFormatTime(getTime());
        message.fileId = ""; // 目前先设置为空字符串，后续通过其他途径设置
        message.fileName = ""; // 不使用

        return message;
    }
}; // end Message

///////////////////////////////////////////////////////////
/// 聊天会话信息
///////////////////////////////////////////////////////////
class ChatSessionInfo {
public:
    QString chatSessionId = "";     // 聊天会话id
    QString chatSessionName = "";   // 聊天会话名称，当会话为单聊会话时该字段为对方用户的昵称
    Message lastMessage;            // 会话中的最新一条消息，目的是为了预览
    QIcon avatar;                   // 会话头像，如果会话时单聊，头像为对方的头像；如果会话时群聊，头像为群聊的头像（头像设置问题TODO???）
    QString userId = "";            // 用户id，如果会话时单聊，该字段被设置；如果会话时群聊，该字段为空串。后续可通过该字段区分会话类型

    void load(const bite_im::ChatSessionInfo& chatSessionInfo)
    {
        this->chatSessionId = chatSessionInfo.chatSessionId();
        this->chatSessionName = chatSessionInfo.chatSessionName();

        if (chatSessionInfo.hasSingleChatFriendId())
        {
            this->userId = chatSessionInfo.singleChatFriendId();
        }

        if (chatSessionInfo.hasPrevMessage())
        {
            lastMessage.load(chatSessionInfo.prevMessage());
        }

        if (chatSessionInfo.hasAvatar() && !chatSessionInfo.avatar().isEmpty())
        {
            // 已经有头像了, 直接设置这个头像
            this->avatar = makeIcon(chatSessionInfo.avatar());
        }
        else
        {
            // 如果没有头像, 则根据当前会话是单聊还是群聊, 使用不同的默认头像.
            if (userId != "") {
                // 单聊
                this->avatar = QIcon(":/resource/image/defaultAvatar.png");
            } else {
                // 群聊
                this->avatar = QIcon(":/resource/image/groupAvatar.png");
            }
        }
    }
}; // end CahtSessionInfo

} // end model

#endif // DATA_H
